package cn.simple.filetools.poi.excel.support;

import cn.simple.filetools.annotation.Excels;
import cn.simple.filetools.exception.NoAnnotationException;
import cn.simple.filetools.exception.ReadExcelException;
import cn.simple.filetools.poi.excel.PoiExcelDataConvertProcessor;
import cn.simple.filetools.poi.excel.PoiExcelReadListener;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;

/**
 * @author jiangjinjian
 * @date 2024/8/27 17:32
 * @description 抽象poi-excel读取监听器
 */
@Slf4j
public abstract class AbstractPoiExcelReadListener<T> implements PoiExcelReadListener<T> {

    private final PoiExcelDataConvertProcessor poiExcelDataConvertProcessor;

    @Getter
    private final Class<T> typeClass;

    protected AbstractPoiExcelReadListener(Class<T> typeClass, PoiExcelDataConvertProcessor poiExcelDataConvertProcessor) {
        this.typeClass = typeClass;
        this.poiExcelDataConvertProcessor = poiExcelDataConvertProcessor;
    }


    @Override
    public void read(String path, int sheetNum, int headRow) {
        try {
            InputStream is = Files.newInputStream(Paths.get(path));
            read(is, sheetNum, headRow);
        } catch (IOException e) {
            log.error("读取excel异常：" + e.getMessage(), e);
            throw new ReadExcelException(e);
        }
    }

    @Override
    public void read(InputStream is, int sheetNum, int headRow) {
        //5.2、创建工作薄
        try {
            Workbook workbook = WorkbookFactory.create(is);
            Sheet sheet = workbook.getSheetAt(sheetNum);
            //5.4.1、获取sheet中有数据的行数
            int rows = sheet.getPhysicalNumberOfRows();
            for (int i = 0; i < rows; i++) {
                //获取当前行
                Row row = sheet.getRow(i);
                if (i < headRow) {
                    //处理出当前行的标有map结构
                    Map<Integer, String> headMap = lineDataAsMap(row);
                    //提供监听表头方法
                    listenHead(headMap);
                } else {
                    //将行数据处理成对象
                    T entity = lineDataAsClass(row);
                    //监听每一行数据
                    listenData(entity);
                }
            }
        } catch (IOException e) {
            log.error("读取excel异常：" + e.getMessage(), e);
            throw new ReadExcelException(e);
        }
    }


    @Override
    public void readByAnnotation(InputStream is) {
        Excels annotation = typeClass.getAnnotation(Excels.class);
        if (annotation == null) {
            throw new NoAnnotationException(String.format("类型[%s]缺少[Excels]注解", typeClass.getTypeName()));
        }
        // todo 适配传sheet名称的实现
        read(is, annotation.sheetNum(), annotation.headRows());
    }

    /**
     * 监听行数据
     *
     * @param lineData 行数据
     */
    protected abstract void listenData(T lineData);

    /**
     * 监听表头
     *
     * @param headMap 表头map，map结构{列数，从0开始，列头名}
     */
    protected abstract void listenHead(Map<Integer, String> headMap);

    /**
     * 将一行数据处理成类的泛型
     *
     * @param currRow 当前行
     * @return
     */
    private T lineDataAsClass(Row currRow) {
        return poiExcelDataConvertProcessor.convertAsClass(currRow, typeClass);
    }

    /**
     * 获取某一行的表头，map结构{列下标，列值}
     *
     * @param currRow 当前行
     * @return
     */
    private Map<Integer, String> lineDataAsMap(Row currRow) {
        return poiExcelDataConvertProcessor.convertAsMap(currRow);
    }
}
